#include "PhysicsMesh.h"
#include "ObjPhysicsReader.h"
#include "InputMesh.h"
#include "D3DUtil.h"
#include "Level.h"
#include "GraphCamera.h"
#include "CookingStream.h"

PhysicsMesh::PhysicsMesh(tstring filename, tstring physicsFileName, float3 loc, float3 rot, Type type, PhysX *pPhysX) : LevelElement(), m_filename(filename), m_physicsFileName(physicsFileName), m_Pos(loc), m_Rot(rot),m_Type(type), m_pPhysX(pPhysX), m_pActor(NULL), m_pVertexLayout(0), m_pDefaultEffect(NULL), m_pDefaultTechnique(NULL), m_VertexBufferStride(0)
{
	Translate(D3DXVECTOR3(loc.x,loc.y,loc.z));
}

PhysicsMesh::~PhysicsMesh(void)
{
	for (UINT i = 0; i<m_Meshes.size();++i)
		delete m_Meshes[i];

	for (UINT i = 0; i<m_pVertexBuffers.size();++i)
		m_pVertexBuffers[i]->Release();

	for (UINT i = 0; i<m_pIndexBuffers.size();++i)
		m_pIndexBuffers[i]->Release();

	SafeRelease(m_pVertexLayout);
}

void PhysicsMesh::Initialize(ContentManager *pContentManager)
{
	ObjPhysicsReader reader(m_filename);
	if(!reader.Read())
		return;

	m_Meshes.assign(reader.GetInputMeshBegin(), reader.GetInputMeshEnd());

	if (m_Type == Concave)
		BuildConcave();
	else
		BuildConvex();

	CreateEffect(pContentManager);
	DefineInputlayout();
	BuildVertexBuffer();
	BuildIndexBuffer();
	Rotate(D3DXVECTOR3(m_Rot.x,m_Rot.y,m_Rot.z));
	SetPhysXGlobalPose();
}

void PhysicsMesh::CreateEffect(ContentManager *pContentManager)
{
	m_pDefaultEffect = pContentManager->GetEffect(m_pLevel->GetDevice(), _T("./Effect/checker.fx"));	

	//get first technique found
	m_pDefaultTechnique = m_pDefaultEffect->GetTechniqueByIndex(0);

	GetEffectVariables(m_pDefaultEffect);
}

bool PhysicsMesh::BuildConcave()
{
	string str = string(m_physicsFileName.begin(), m_physicsFileName.end());
	NxTriangleMesh *pTriangleMesh = m_pPhysX->GetSDK()->createTriangleMesh(UserStream(str.c_str(),true));

	//create triangle mesh instance
	NxTriangleMeshShapeDesc TriangleMeshShapeDesc;

	//calc mass, using AABB volume
	TriangleMeshShapeDesc.mass = 200;
	//set meshdata
	TriangleMeshShapeDesc.meshData  = pTriangleMesh; //See NxTriangleMesh

	NxActorDesc ActorDesc;
	ActorDesc.shapes.push_back(&TriangleMeshShapeDesc);
	ActorDesc.globalPose.t = NxVec3( 0,0,0 );
	ActorDesc.body=0;

	m_pActor = m_pPhysX->GetScene()->createActor(ActorDesc);

	return m_pActor != 0;
}

bool PhysicsMesh::BuildConvex()
{
	string str = string(m_physicsFileName.begin(), m_physicsFileName.end());
	NxConvexMesh *pConvexMesh = m_pPhysX->GetSDK()->createConvexMesh(UserStream(str.c_str(),true));

	//create triangle mesh instance
	NxConvexShapeDesc ConvexShapeDesc;

	//calc mass, using AABB volume
	ConvexShapeDesc.mass = 200;
	//set meshdata
	ConvexShapeDesc.meshData  = pConvexMesh; //See NxConvexMesh

	NxActorDesc ActorDesc;
	ActorDesc.shapes.push_back(&ConvexShapeDesc);
	ActorDesc.globalPose.t = NxVec3( 0,0,0 );
	ActorDesc.body=0;

	m_pActor = m_pPhysX->GetScene()->createActor(ActorDesc);

	return m_pActor != 0;
}

void PhysicsMesh::BuildVertexBuffer()
{
	for (MIT it = m_Meshes.begin(); it < m_Meshes.end();++it)
	{
		InputMesh* mesh = *it;
		int m_NumVertices = (*it)->GetNumVertices();

		D3D10_BUFFER_DESC bd;
		bd.Usage = D3D10_USAGE_DEFAULT;
		bd.ByteWidth = sizeof(float3) * m_NumVertices;
		bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
		bd.CPUAccessFlags = 0;
		bd.MiscFlags = 0;

		D3D10_SUBRESOURCE_DATA InitData;
		InitData.pSysMem = (*it)->GetVertexPointer();
		//create a ID3D10Buffer in graphics memory containing the vertex info
		ID3D10Buffer* pTempVertexBuffer;
		HR(m_pLevel->GetDevice()->CreateBuffer( &bd, &InitData, &pTempVertexBuffer ));

		m_pVertexBuffers.push_back(pTempVertexBuffer);
	}
}

void PhysicsMesh::BuildIndexBuffer()
{
	for (MIT it = m_Meshes.begin(); it < m_Meshes.end();++it)
	{
		D3D10_BUFFER_DESC bd;
		bd.Usage = D3D10_USAGE_DEFAULT;
		bd.ByteWidth = sizeof( int ) * (*it)->GetNumFaces()*3;        
		bd.BindFlags = D3D10_BIND_INDEX_BUFFER;
		bd.CPUAccessFlags = 0;
		bd.MiscFlags = 0;

		D3D10_SUBRESOURCE_DATA InitData;
		InitData.pSysMem = (*it)->GetTrianglesPointer();
		ID3D10Buffer* pTempIndexBuffer;
		HR(m_pLevel->GetDevice()->CreateBuffer( &bd, &InitData, &pTempIndexBuffer ));
		m_pIndexBuffers.push_back(pTempIndexBuffer);
	}
}

void PhysicsMesh::GetEffectVariables(ID3D10Effect* effect)
{
	m_pDefaultTechnique = effect->GetTechniqueByName("CheckersTech");
	m_pWorldViewProjectionVariable = effect->GetVariableByName("gWVP")->AsMatrix();
}

void PhysicsMesh::SetPhysXGlobalPose()
{
	SetPhysXGlobalPose(m_World);
}

void PhysicsMesh::SetPhysXGlobalPose(const D3DXMATRIX& World)
{
	NxMat34 mat;
	mat.setColumnMajor44(World);
	if(m_pActor !=0) m_pActor->setGlobalPose(mat);
}

void PhysicsMesh::DefineInputlayout()
{
	// Define the input layout
	D3D10_INPUT_ELEMENT_DESC layout[] =
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	};

	UINT numElements = sizeof(layout)/sizeof(layout[0]);

	// Create the input layout
	D3D10_PASS_DESC PassDesc;
	// Get the pass decriptor from the effect technique
	m_pDefaultTechnique->GetPassByIndex(0)->GetDesc( &PassDesc );
	HR(m_pLevel->GetDevice()->CreateInputLayout(layout, numElements, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &m_pVertexLayout));

	//important set the size of the vertexbuffer, is used in draw base member function
	m_VertexBufferStride = sizeof(float3);
}

void PhysicsMesh::Tick(const InputState & refInputState)
{
	if ( m_pActor != 0 )
	{
		m_pActor->getGlobalPose().getColumnMajor44((NxF32*)&m_World.m);
	}
}

void PhysicsMesh::Draw(const RenderContext* pRenderContext)
{
	ASSERT(m_pVertexBuffers.size() == m_pIndexBuffers.size());
	ASSERT(m_pVertexBuffers.size() == m_Meshes.size());

	if(!m_pDefaultTechnique )
	{
		MessageBox(0,_T("No Technique"),_T("ERROR"),0);
		return;
	}

	//Update Shader matrix variables
	D3DXMATRIX matView = pRenderContext->GetCamera()->GetView();
	D3DXMATRIX matProj = pRenderContext->GetCamera()->GetProj();
	m_pWorldViewProjectionVariable->SetMatrix((float*)&(m_World*matView*matProj));

	// Set the input layout
	m_pLevel->GetDevice()->IASetInputLayout( m_pVertexLayout );

	// Set primitive topology
	m_pLevel->GetDevice()->IASetPrimitiveTopology( D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST );

	for (UINT i = 0; i< m_pVertexBuffers.size(); ++i)
	{
		// Set vertex buffer
		UINT offset = 0;
		m_pLevel->GetDevice()->IASetVertexBuffers( 0, 1, &m_pVertexBuffers[i], &m_VertexBufferStride, &offset );

		// Set index buffer
		m_pLevel->GetDevice()->IASetIndexBuffer( m_pIndexBuffers[i], DXGI_FORMAT_R32_UINT, 0 );

		//
		// Render
		//
		D3D10_TECHNIQUE_DESC techDesc;
		((m_pDefaultTechnique))->GetDesc( &techDesc );
		for( UINT p = 0; p < techDesc.Passes; ++p )
		{
			((m_pDefaultTechnique))->GetPassByIndex( p )->Apply(0);
			m_pLevel->GetDevice()->DrawIndexed( m_Meshes[i]->GetNumFaces()*3, 0, 0 );
		}
	}
}
NxActor* PhysicsMesh::GetActor()
{ 
	return m_pActor;
}